#  uvicorn Main:app --host 0.0.0.0 --port 8000 --reload  启动
import asyncio
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request, HTTPException, Query
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
import aiomysql
from config import settings
from WebsocketClient import WebsocketClient
from HttpMgr import HttpMgr
from pydantic import BaseModel, Field
from typing import List
from datetime import datetime
from EventCompanyClient import EventCompanyClient


app = FastAPI()


# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 挂载静态文件
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/templates", StaticFiles(directory="static/templates"), name="templates")

@app.get("/admin")
async def admin_page():
    return FileResponse("static/admin.html")

# MySQL连接池
pool = None
ws_client = WebsocketClient()

async def create_db_pool():
    global pool
    pool = await aiomysql.create_pool(
        host=settings.DB_HOST,
        port=settings.DB_PORT,
        user=settings.DB_USER,
        password=settings.DB_PASSWORD,
        db=settings.DB_NAME,
        minsize=5,
        maxsize=20,
    )

@app.on_event("startup")
async def startup():
    await create_db_pool()
    print("服务器已启动，访问 http://localhost:8000/admin 进行管理")

@app.websocket("/admin/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            # 在此处理WebSocket消息
            await websocket.send_text(f"收到消息: {data}")
    except WebSocketDisconnect:
        print("客户端断开连接")

from pydantic import BaseModel

class PlayerQuery(BaseModel):
    page: int = 1
    page_size: int = 50
    sort_by: str = 'start_number'

@app.get('/admin/players')
async def get_players(query: PlayerQuery):
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    await cur.execute(
                        "SELECT * FROM records ORDER BY %s LIMIT %s OFFSET %s",
                        (query.sort_by, query.page_size, (query.page - 1) * query.page_size)
                    )
                    result = await cur.fetchall()
                    break
        except aiomysql.OperationalError:
            await create_db_pool()
            continue
    return {"data": result, "total": len(result)}

# @app.get('/admin/monitor')
# async def admin_monitor_page():
#     return FileResponse("static/admin_monitor.html")

# @app.get('/admin/players')
# async def players_page():
#     return FileResponse('static/players.html')

# @app.get('/admin/{path:path}')
# async def admin_wrapper():
#     return FileResponse('static/admin.html')

@app.get("/api/ws/status")
async def get_ws_status():
    return {"is_connected": ws_client.is_connected}

class SubmitPassResultItem(BaseModel):
    datatype: int = Field(..., description="过点类型编码")
    playerId: str = Field(..., min_length=1, description="选手ID")
    name: str = Field(..., max_length=50, description="选手姓名")
    time: str = Field(..., regex="^\\d{2}:\\d{2}:\\d{2}$", description="过点时间")
    country: str = Field(default="中国", max_length=20, description="国籍")
    gender: int = Field(default=1, ge=0, le=1, description="性别，0为女，1为男")

@app.post("/api/submit-pass-result")
async def submit_pass_result(data: List[SubmitPassResultItem]):
    try:
        for _ in range(3):  # 最大重试3次
            try:
                async with pool.acquire() as conn:
                    async with conn.cursor(aiomysql.DictCursor) as cur:
                        batch_data = [
                            (
                                item.datatype,
                                item.playerId,
                                item.name,
                                item.time,
                                item.country,
                                item.gender
                            ) for item in data
                        ]
                        print(f"准备插入数据: {batch_data}")
                        for bdata in batch_data:
                            try:
                                await cur.execute("""
                                    INSERT INTO curPassResult (type, id, name, time, country, gender, timestamp)
                                    VALUES (%s, %s, %s, %s, %s, %s, NOW())
                                """, bdata)
                            except aiomysql.OperationalError:
                                await create_db_pool()  # 重建连接池
                                continue
                            except aiomysql.IntegrityError as e:
                                print(f"[唯一性校验] 重复数据已跳过: type={bdata[0]} id={bdata[1]}")
                                continue
                    await conn.commit()
                    return {"status": "success", "inserted": len(data)}
            except Exception as e:
                print(f"插入数据时发生错误: {str(e)}")
                continue
    except Exception as e:
        await conn.rollback()
        raise HTTPException(
            status_code=500,
            detail={"code": "DB_ERROR", "message": f"数据库操作失败: {str(e)}"}
        )

class ControlAction(BaseModel):
    action: str

@app.post("/api/ws/control")
async def control_websocket(request: ControlAction):
    print(f"试图控制websocket {request.action}")
    if request.action == "start":
        await ws_client.connect()
    elif request.action == "stop":
        await ws_client.close()
    elif request.action == "reconnect":
        await ws_client.reconnect()
    else:
        raise HTTPException(status_code=400, detail={"code": "INVALID_ACTION", "message": "无效操作"})
    return {"status": "success"}


@app.get("/api/playerlist")
async def get_playerlist():
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    await cur.execute("SELECT * FROM curPlayerList")
                    result = await cur.fetchall()
                    print(result)
                    return result
                    break
        except aiomysql.OperationalError:
            await create_db_pool()
            continue

class PlayerItem(BaseModel):
    playerid: str
    name: str
    country: str
    gender: int = 1  # 默认值为1，表示男

@app.post("/api/upload-playerlist")
async def upload_playerlist(players: List[PlayerItem]):
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    batch_data = [
                        (player.playerid, player.name, player.country, player.gender) 
                        for player in players
                    ]
                    await cur.executemany(
                        "INSERT INTO curPlayerList (id, name, country, gender) VALUES (%s, %s, %s, %s)",
                        batch_data
                    )
                    await conn.commit()
                    return {"status": "success", "inserted": len(batch_data)}
                    break
        except aiomysql.OperationalError:
            await create_db_pool()
            continue
        except Exception as e:
            await conn.rollback()
            raise HTTPException(
                status_code=500,
                detail={"code": "DB_ERROR", "message": f"数据库操作失败: {str(e)}"}
            )

@app.get("/api/passdata")
async def get_passdata():
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    print("到这里来了get_passdata！取到了这么个东西")
                    await cur.execute("SELECT * FROM curPassDatas")
                    result = await cur.fetchall()
                    print(result)
                    return result         
                    break
        except aiomysql.OperationalError:
            await create_db_pool()
            continue
    return result      

    
@app.get("/api/passResult")
async def get_passResult():
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    print("到这里来了get_passResult！取到了这么个东西")
                    await cur.execute("SELECT * FROM curPassResult")
                    result = await cur.fetchall()
                    print(result)
                    return result         
                    break
        except aiomysql.OperationalError:
            await create_db_pool()
            continue
    return result     
    
@app.get("/api/resetMatch")
async def reset_match():
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    print("开始删除")
                    await cur.execute("DELETE FROM mlsdata.curPassDatas WHERE idpassDatas > 0")
                    await cur.execute("DELETE FROM mlsdata.curPassResult WHERE idcurPassResult > 0")
                    await conn.commit()
                    return {"status": "success", "message": "删除操作成功"}
                    break
        except aiomysql.OperationalError as e:
            print(e)
            await create_db_pool()
            continue

@app.get("/api/clearPlayers")
async def clear_players():
    for _ in range(3):  # 最大重试3次
        try:
            async with pool.acquire() as conn:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    await cur.execute("DELETE FROM mlsdata.curPlayerList WHERE idPlayerList > 0")
                    await conn.commit()
                    return {"status": "success", "message": "删除操作成功"}
                    break
        except aiomysql.OperationalError as e:
            print(e)
            await create_db_pool()
            continue

# 轮滑半马当时非常临时临时的一个小内部启动
# 全局变量保存半马相关资源
_half_marathon_client = None
_half_marathon_task = None

@app.get("/api/startHalfMarathon")
async def start_half_marathon():
    global _half_marathon_client, _half_marathon_task
    print("可以start")
    # _half_marathon_client = EventCompanyClient(base_url="http://timing.thinkgo-iot.cc", 
    #                          username="pg", 
    #                          pwd="Admin@123")
    _half_marathon_client = EventCompanyClient(base_url="http://cluster.rts.thinkgo-iot.cc", 
                              username="kd", 
                              pwd="123456")
    await _half_marathon_client._login()
    print(f"登录成功，token: {_half_marathon_client.access_token}")
    matchid = 1
    await clear_players()
    ret = await _half_marathon_client.get_players(id=matchid)
    print(f"获取选手列表: {ret}")
    
    async def poll_results():
        while True:
            try:
                ret = await _half_marathon_client.get_results_by_positions(id=matchid, bib="", pos=[1])
                #print(f"获取位置结果: {ret}")
            except Exception as e:
                print(f"获取位置结果失败: {e}")
            await asyncio.sleep(2)
            try:
                ret = await _half_marathon_client.get_results_by_positions(id=matchid, bib="", pos=[2])
                #print(f"获取位置结果: {ret}")
            except Exception as e:
                print(f"获取位置结果失败: {e}")
            await asyncio.sleep(2)
            try:
                ret = await _half_marathon_client.get_results_by_positions(id=matchid, bib="", pos=[6])
                #print(f"获取位置结果: {ret}")
            except Exception as e:
                print(f"获取位置结果失败: {e}")
            await asyncio.sleep(2)
            try:
                ret = await _half_marathon_client.get_results_by_positions(id=matchid, bib="", pos=[8])
                #print(f"获取位置结果: {ret}")
            except Exception as e:
                print(f"获取位置结果失败: {e}")
            await asyncio.sleep(2)
            try:
                ret = await _half_marathon_client.get_results_by_positions(id=matchid, bib="", pos=[99])
                #print(f"获取位置结果: {ret}")
            except Exception as e:
                print(f"获取位置结果失败: {e}")
            await asyncio.sleep(2)
    
    _half_marathon_task = asyncio.create_task(poll_results())
    return {"status": "success", "message": "后台任务已启动"}
    
@app.get("/api/stopHalfMarathon")
async def stop_half_marathon():
    global _half_marathon_client, _half_marathon_task
    await clear_players()
    if _half_marathon_task:
        _half_marathon_task.cancel()
        try:
            await _half_marathon_task
        except asyncio.CancelledError:
            print("半马轮询任务已取消")
        _half_marathon_task = None
    
    if _half_marathon_client:
        # await _half_marathon_client.close()
        _half_marathon_client = None
        print("半马客户端已关闭")
    
    return {"status": "success", "message": "半马轮询已停止"}
    

@app.get('/test')
async def test_config():
    devices = HttpMgr._instance.device_cache if HttpMgr._instance else {}
    return {
        "config_source": "通过单例注入",
        "device_mapping": devices,
        "说明": "业务层仅依赖HttpMgr接口，具体配置来源可替换"
    }

# 提供给系统外面调用的http接口
# 添加API路由
@app.get("/myapi/config", tags=["设备管理"])
async def get_device_config(score_type: str = Query(..., min_length=3, max_length=20, regex="^[a-zA-Z0-9_]+")):
    if not HttpMgr._instance:
        raise HTTPException(status_code=503, detail="服务未初始化")
    
    config = HttpMgr._instance.device_cache.get(score_type)
    if not config:
        raise HTTPException(status_code=404, detail="配置不存在")
    
    return {
        "score_type": score_type,
        "mac_addresses": config
    }


@app.post("/myapi/passdata", tags=["获取过点数据"])
async def report_passdata(
        report_type: str = Query(..., min_length=1, example="0", description="过点类型"),
        report_id: int = Query(..., gt=0, example=0, description="表内起始索引id")
    ):
    try:
        from Main import pool
        for _ in range(3):  # 最大重试3次
            try:
                async with pool.acquire() as conn:
                    async with conn.cursor(aiomysql.DictCursor) as cur:
                        await cur.execute("""
                                SELECT idpassDatas, name, id, type, time FROM curPlayerList 
                                WHERE idpassDatas > %s AND type = %s
                            """, (report_id, report_type))
                        params = [
                            {
                                "idpassDatas": row['idpassDatas'],
                                "id": row['id'],
                                "name": row['name'],
                                "type": row['type'],
                                "time": row['time']
                            }
                            for row in await cur.fetchall()
                        ]
                        return {"code":200,"data": params} 
                        break
            except aiomysql.OperationalError:
                await create_db_pool()
                continue  
        return {"code": -1, "message": "get passdata error"}
    except Exception as e:
        return {"code": 500, "message": str(e)}


class RequestPassData10(BaseModel):
    report_type: str
    gender: int   #0  1
@app.post("/myapi/passdata10", tags=["获取过点前十数据"])
async def report_passdata10(data: RequestPassData10):  # 接收 JSON
    report_type = data.report_type
    gender = data.gender
    print(report_type)
    try:
        from Main import pool
        for _ in range(3):  # 最大重试3次
            try:
                async with pool.acquire() as conn:
                    async with conn.cursor(aiomysql.DictCursor) as cur:
                        await cur.execute("""
                                SELECT idcurPassResult, name, id, type, time, country, gender FROM curPassResult 
                                WHERE type = %s AND gender = %s
                                ORDER BY time ASC 
                                LIMIT 10
                            """, (report_type, gender))
                        params = [
                            {
                                "idcurPassResult": row['idcurPassResult'],
                                "id": row['id'],
                                "name": row['name'],
                                "type": row['type'],
                                "time": row['time'],
                                "country": row['country'],
                                "gender": row['gender']
                            }
                            for row in await cur.fetchall()
                        ]
                        print(params)
                        # 补充空数据到10条
                        while len(params) < 10:
                            params.append({
                                "idcurPassResult": 0,
                                "id": "-",
                                "name": "-",
                                "type": "-",
                                "time": "--:--:--",
                                "country": "-",
                                "gender": "-"
                            })
                        return {"code":200,"data": params} 
                        break
            except aiomysql.OperationalError as e:
                print(e)
                await create_db_pool()
                continue  
        return {"code": -1, "message": "get passdata error"}
    except Exception as e:
        print(e)
        return {"code": 500, "message": str(e)}

class RequestPlayerResult(BaseModel):
    playerid: str
@app.post("/myapi/playerResult", tags=["获取某选手全成绩"])
async def report_playerResult(data: RequestPlayerResult):  # 接收 JSON
    playerid = data.playerid
    print(playerid)
    try:
        from Main import pool
        for _ in range(3):  # 最大重试3次
            try:
                async with pool.acquire() as conn:
                    async with conn.cursor(aiomysql.DictCursor) as cur:
                        await cur.execute("""
                                SELECT name, type, time, gender, country
                                FROM mlsdata.curPassResult 
                                WHERE id = %s
                                ORDER BY type ASC
                            """, playerid)

                        # 生成0-42类型模板
                        type_template = [{"type": str(t), "time": "--:--:--"} for t in [0,5,10,15,20,25,30,35,40,42]]
                        
                        # 用实际数据更新模板
                        rows = await cur.fetchall()
                        print(rows)
                        db_results = {row['type']: row['time'] for row in rows}
                        for item in type_template:
                            if item["type"] in db_results:
                                item["time"] = db_results[item["type"]]
                        
                        # 构造返回数据结构
                        params = {
                            "playerid": playerid,
                            "playername": rows[0]['name'] if rows else "-",
                            "gender": rows[0]['gender'] if rows else "-",
                            "country": rows[0]['country'] if rows else "-",
                            "results": type_template
                        }
                        print(params)

                        return {"code":200,"data": params} 
                        break
            except aiomysql.OperationalError as e:
                print(e)
                await create_db_pool()
                continue  
        return {"code": -1, "message": "get passdata error"}
    except Exception as e:
        print(e)
        return {"code": 500, "message": str(e)}

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)